package org.liu.simpleTank;

import java.util.Arrays;
import java.util.Collection;
import java.util.EventObject;
import java.util.EventListener;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Set;
import java.util.HashSet;

public class Bullet extends MovableElement implements Hittable {
	
	private double speed;
	public static final double SPEED_NORMAL = 0.01;
	public static final double SPEED_FAST = 0.02;
	
	public Bullet(double speed, int dir) {
		super(CONSTS.BULLET, 1, 1);
		if(dir == CONSTS.UP || dir == CONSTS.DOWN) // vertical direction
			this.w = 2;
		else // horizontal direction
			this.h = 2;
		this.speed = speed;
		this.direction = dir;
		setMoving();
	}
	
	@Override
	public void update(UpdateContext ctx) {
		int i = addProgress(ctx.getDelta() * speed);
		while(i-- > 0) {
			if(!expand(direction)) { // reaches the boundary of the map
				destroy();
				return;
			}
			rmOpposite(direction);
		}
		
		Set<Element> els = Cell.cellsGet(allCell());
		HashSet<Hittable> targets = new HashSet<Hittable>(4);
		Class hittable = null;
		try {
			hittable = Class.forName("org.liu.simpleTank.Hittable");
		} catch (ClassNotFoundException e) { }
		Iterator<Element> iter = els.iterator();
		while(iter.hasNext()) {
			Element el = iter.next();
			if(Util.arrayContains(el.getClass().getInterfaces(), hittable))
				targets.add((Hittable)el);
		}
		if(!targets.isEmpty()) {
			Hittable[] targetArr = new Hittable[targets.size()];
			hit(targets.toArray(targetArr));
		}
	}
	
	// Bullet Events
	public static class HitEvent extends EventObject {
		private Hittable[] targets;
		private boolean destroyFlag;
		public HitEvent(Object source, Hittable[] targets) {
			super(source);
			destroyFlag = true;
			this.targets = targets;
		}
		public Hittable[] getTargets() { return targets; }
		public void setDestroy(boolean b) { destroyFlag = b; }
	}

	public static abstract class BulletListener implements EventListener {
		public void hit(HitEvent e) { }
	}
	
	private ArrayList<BulletListener> listeners;
	{
		listeners = new ArrayList<Bullet.BulletListener>();
	}
	
	public void addListener(BulletListener listener) {
		listeners.add(listener);
	}

	private void hit(Hittable[] targets) {
//		target.hit(); // make target know that he is hit (Leave this to FireManger 
		// for more flex control)
		Iterator<BulletListener> iter = listeners.iterator();
		HitEvent e;
		boolean shouldDestroyed = false;
		while(iter.hasNext()) {
			e = new HitEvent(this, targets);
			iter.next().hit(e);
			if(e.destroyFlag)
				shouldDestroyed = true;
		}
		if(shouldDestroyed)
			destroy();
	}
	
	// remove listeners after destruction
	@Override
	public void destroy() {
		super.destroy();
		listeners.clear();
	}
	
	// Hittable interface implementation
	@Override
	public int getLife() { return 1; }
	@Override
	public void setLife(int life) { }
	@Override
	public int incLife() { return getLife(); }
	@Override
	public void hit() { destroy(); }
}
